#include "libnnet.h"


// create a socket for a tcp server
// return -1 on socket error
// return -2 on bind error
int     nnet_make_tcp_server_socket(uint16_t port)
{
  int sock;
  struct sockaddr_in name;
  
  /* Create the socket. */
  sock = socket(PF_INET, SOCK_STREAM, 0);
  if(sock < 0)
    {
      perror("socket");
      return -1;
    }
  
  /* Give the socket a name. */
  name.sin_family = AF_INET;
  name.sin_port = htons(port);
  name.sin_addr.s_addr = htonl(INADDR_ANY);
  if(bind(sock, (struct sockaddr *)&name, sizeof (name)) < 0)
    {
      perror("bind");
      return -2;
    }
  
  return sock;
}


// create a select server on port: port, and process requests via
// the function set in request_handler.
// return -1 on socket error
// return -2 on bind error
// return -3 on listen error
// return -4 on select error
// return -5 on accept error
//
// the request handler must return 0 if it want to keep the connection open
// or -1 if it want to close it.
int     nnet_create_tcp_select_server(int port
                                      , nnetrqsth_t *request_handler)
{
  //extern int make_socket (uint16_t port);
  int sock;
  fd_set active_fd_set, read_fd_set;
  int i;
  struct sockaddr_in clientname;
  size_t size;
     
  /* Create the socket and set it up to accept connections. */
  sock = nnet_make_tcp_server_socket(port);
  if(sock < 0)
    return sock;
  if(listen(sock, 1) < 0)
    {
      perror("listen");
      return -3;
    }
  
  /* Initialize the set of active sockets. */
  FD_ZERO(&active_fd_set);
  FD_SET(sock, &active_fd_set);
     
  while(1)
    {
      /* Block until input arrives on one or more active sockets. */
      read_fd_set = active_fd_set;
      if (select(FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
        {
          perror ("select");
          return -4;
        }
     
      /* Service all the sockets with input pending. */
      for(i = 0; i < FD_SETSIZE; ++i)
        if(FD_ISSET (i, &read_fd_set))
          {
            if (i == sock)
              {
                /* Connection request on original socket. */
                int new;
                size = sizeof (clientname);
                new = accept (sock,
                              (struct sockaddr *) &clientname,
                              (socklen_t *)&size);
                if(new < 0)
                  {
                    perror ("accept");
                    return -5;
                  }
                fprintf(stderr,
                        "Tcp connect server: connect from host %s, port %hd.\n",
                        inet_ntoa(clientname.sin_addr),
                        ntohs(clientname.sin_port));
                FD_SET(new, &active_fd_set);
              }
            else
              {
                /* Data arriving on an already-connected socket. */
                if(request_handler->handler(request_handler->params, i) < 0)
                  {
                    close(i);
                    FD_CLR(i, &active_fd_set);
                  }
              }
          }
    }
}

